spring中存在三种循环依赖: 构造器循环依赖: 这种情况 spring 无法处理,将抛出BeanCurrentlylnCreationException异常 单例 Bean setter 循环依赖, 通过三级缓存来解决, 这也是本篇博客描述的地方 非单例循环依赖(如 propertype), 无法提前暴露 Bean, 无法解决 spring 单例对象的初始化过程 spring 单例对象的实例化、初始化过程是在 initializeBean: 回调Bean的方法(postProcessBeforeInitialization->init->postProcessAfterInitialization) 构造器循环依赖 bean 池”中,在创建过程中将一直保持在这个池中,因此如果在创建bean 过程中发现自己已经在”当前创建bean 池” 里时,将抛出BeanCurrentlylnCreationException异常表示循环依赖 ;而对于创建完毕的bean 将从”当前创建bean 池”中清除掉 setter循环依赖的处理 spring 使用三级缓存来解决单例 setter 循环依赖: private final Map singletonObjects
Spring为什么不能解决多例的循环依赖 什么是循环依赖 循环依赖分为两种,一种是构造器的相互依赖,另外一种是属性的相互依赖。 public A(B b){} } public class B{ private B(A a){} } 如何解决 Spring只能解决属性的循环依赖,构造器的循环依赖是不能解决的。 spring中解决循环依赖的核心思想就是利用三级缓存,先创建Bean,后为各个属性赋值具体什么是三级缓存呢? 为什么不能解决构造器的循环依赖 Spring解决循环依赖主要是依赖三级缓存,但是的在调用构造方法之前还未将其放入三级缓存之中,因此后续的依赖调用构造方法的时候并不能从三级缓存中获取到依赖的Bean,因此不能解决 Spring为什么不能解决多例的循环依赖 多实例Bean是每次调用一次getBean都会执行一次构造方法并且未属性赋值,根本没有三级缓存,因此解决循环依赖。
什么是循环依赖 循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比如A引用B,B引用C,C引用A,则它们最终反映为一个环。 spring 中循环依赖注入分三种情况 构造器循环依赖 setter方法循环注入 2.1 setter方法注入 单例模式(scope=singleton) 2.2 setter方法注入 非单例模式 构造器循环依赖 表示通过构造器注入构成的循环依赖,此依赖是无法解决的,只能抛出BeanCurrentlyInCreationException异常表示循环依赖。 2. setter方法循环注入 setter循环依赖:表示通过setter注入方式构成的循环依赖。 对于setter注入造成的依赖是通过Spring容器提前暴露刚完成构造器注入但未完成其他步骤(如setter注入)的Bean来完成的,而且只能解决单例作用域的Bean循环依赖。
在本文中,我们将深入探讨Spring循环依赖的原理、处理机制、最佳实践以及可能遇到的问题。 2. 什么是循环依赖? 循环依赖是指两个或多个Bean相互依赖,形成一个闭环。 Spring循环依赖的分类 根据依赖注入的方式不同,循环依赖可以分为以下几种类型: 3.1 构造器循环依赖 构造器循环依赖是指两个或多个Bean通过构造器参数相互依赖。 Spring如何解决循环依赖 Spring框架通过三级缓存(三级缓存机制)来解决大多数情况下的循环依赖问题。 实践中的循环依赖 5.1 避免构造器循环依赖 构造器循环依赖是无法通过Spring的三级缓存机制解决的,因为构造器循环依赖会导致Spring无法实例化任何一个Bean。 Spring循环依赖的潜在问题 尽管Spring可以通过三级缓存机制解决大多数情况下的循环依赖,但在实际开发中,循环依赖仍可能导致一些潜在的问题: 代码难以维护:循环依赖会使代码逻辑复杂,增加代码的维护难度
手写spring循环依赖的整个过程 2. spring怎么解决循环依赖 3. 为什么要二级缓存和三级缓存 4. spring有没有解决构造函数的循环依赖 5. spring有没有解决多例下的循环依赖. 一. 什么是循环依赖? 如下图所示: ? 循环依赖 之 手写代码模拟spring循环依赖 A类依赖了B类, 同时B类有依赖了A类. 这就是循环依赖, 形成了一个闭环 ? 循环依赖 之 手写代码模拟spring循环依赖 如上图: A依赖了B, B同时依赖了A和C , C依赖了A. 这也是循环依赖. , 形成了一个闭环 那么, 如果出现循环依赖, spring是如何解决循环依赖问题的呢? 二. 模拟循环依赖 2.1 复现循环依赖 我们定义三个类: 1.
前言 记录Spring的一些基本理论,引申出Spring循环依赖的问题 Spring是什么 是容器(承载各种bean) 是基石、生态(SpringBoot、SpringCloud都是在此基础上的扩展) 循环依赖的问题 什么是循环依赖 在上文创建Bean对象流程中,放大属性赋值中自定义属性赋值流程,假设有这种情况:自定义对象A引用了自定义对象B,自定义对象B又引用了自定义对象A,这种情况称之为循环依赖( 有种特殊情况,如果A对象中的b属性,是通过构造函授方式注入 ,那么就是在A实例化阶段就需要B对象了,这种情况就无法解决循环依赖的问题! 回顾Bean的生命周期:设计原则是 Bean实例化、属性赋值、初始化之后再去执行AOP生成代理对象 但是为了解决循环依赖但又尽量不打破这个设计原则的情况下,使用了存储了函数式接口的第三级缓存;如果使用二级缓存的话 ;这样只有使用第三级缓存封装一个函数式接口对象到缓存中, 发生循环依赖时,再触发代理类的生成。
</plugins> </build> </project> 以上文件中有2个依赖spring-boot-starter-web,spring-boot-starter-test 起步依赖 这里看到的 spring-boot-starter-xxx就是SpringBoot的起步依赖。 让开发者不在关心Spring相关配置,简化了传统的依赖注入操作。起步依赖本质上是一个Maven项目对象模型,定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。 SpringBoot常规启动都遵循类似的命名模式spring-boot-starter-,其中是一种指定类型的应用程序,如spring-boot-starter-web表示应用程序依赖SpringWeb 例如,mybatis依赖插件引用为mybatis-spring-boot-starter。
什么是循环依赖 循环依赖就是多个Bean之间存在相互依赖,形成一个闭环,如下,PostService和UserService之间就存在相互依赖,这个依赖并不是方法 之间的依赖,而是Bean与Bean之间的依赖 Spring检查循环依赖是在创建Bean的时候检查存放Bean的集合中查看Bean是否已经存在,如果已经存在,则证明Bean已经创建过,就会抛出循环依赖的异常 BeanCurrentlyInCreationException Spring怎么解决循环依赖 Spring为了解决循环依赖问题,引入了三级缓存,如果了解Bean的生命周期,从Bean的生命周期可以知道Bean在实例化的时候会通过Bean的构造函数来实例化Bean( >> singletonFactories = new HashMap<>(16); Spring解决循环依赖流程 Spring要创建PostService这个Bean,会进入AbstractBeanFactory 上面我们说了构造函数Spring不能解决循环依赖问题,Bean为多例也无法解决循环依赖问题,下面来说一下问什么。
前言今天在写代码的时候出现突然出现了一个循环依赖的异常。循环依赖,可能这是一个在日常工作中非常常见的异常。这篇博客的主要目的是用来处理这种循环依赖的情况。 循环分两种情况:一种是直接去改变业务,让业务不会出现那种循环依赖的情况。一种就是使用懒加载的方式,让两个对应的Bean,加载方式起到一个先后顺序这样,处理依赖冲突。 目的我们这一小节主要是在讲如何处理这个循环依赖,从代码方面实现一个懒加载的方式。因为日常工作中我们可能只需要用一个lazy注解就可以处理了。嗯但是很多人还是会用到lombok这种注解的方式。 异常逻辑这是一个出现循环异常的一个依赖类。这里面用到了lombok这种方式去实现一个Bean的注入。
Spring循环依赖问题修复 拆分的时候,把错误都处理完后,准备把工程起起来,发现弹簧的循环依赖问题。 但是在原工程中并没有这个问题,所以一开始怀疑是配置文件的配置不一样,百度了一下这个错误 beanFactory.setAllowRawInjectionDespiteWrapping(true); 看网上说这个配置了,对于循环依赖的这个错误就会解决掉 于是只能调试进去看看 2.调查查看分析 2.1 spring引用的bean和注入的bean不一致导致的这个错误 由于在原工程里是可以循环引用的,所以对工程和新工程都在初始化这两个循环引用的位置进行了调试 earlyProxyReferences不存在第一个的代理对象的值,返回自己的代理对象,结果导致不一样 解决方法 干掉一个AnnotationAwareAspectJAutoProxyCreator,这个循环依赖的错误
org.springframework:spring-test 起步依赖:GroupId :org.springframework.boot 传递依赖 关于spring boot 会不定期在gitbook 更新,欢迎star 地址:spring boot spring-boot-starter org.springframework.boot:spring-boot org.springframework.boot org.springframework.boot:spring-boot-starter org.springframework.boot:spring-boot-actuator spring-boot-starter-amqp :spring-rabbit spring-boot-starter-aop org.springframework.boot:spring-boot-starter org.springframework spring-boot-starter-web org.springframework.boot:spring-boot-starter org.springframework.boot:spring-boot-starter-tomcat
前言Spring 中使用了三级缓存的设计,来解决单例模式下的属性循环依赖问题。 第三级缓存为什么可以解决循环依赖Spring 解决循环依赖的诀窍就在于 singletonFactories 这个三级缓存。 Spring为何不能解决非单例Bean的循环依赖Spring 为何不能解决非单例 Bean 的循环依赖?这个问题可以细分为下面几个问题Spring 为什么不能解决构造器的循环依赖? Spring 为什么不能解决 prototype 作用域循环依赖?Spring 为什么不能解决多例的循环依赖?Spring 为什么不能解决构造器的循环依赖对象的构造函数是在实例化阶段调用的。 而 Spring 中循环依赖的解决正是通过缓存来实现的。非单例Bean的循环依赖如何解决对于构造器注入产生的循环依赖,可以使用 @Lazy 注解,延迟加载。
前言 Spring如何解决的循环依赖,是女同事今天问我的一个问题,其实我很早之前就知道了,但是又有点不知道细节了,那不放大家跟丙丙一起回顾一下。 其实敖丙本人对这类框架源码题还是持一定的怀疑态度的。 那么既然写了这篇文章,闲话少说,发车看看Spring是如何解决的循环依赖,以及带大家看清循环依赖的本质是什么。 正文 通常来说,如果问Spring内部如何解决循环依赖,一定是单默认的单例Bean中,属性互相引用的场景。 比如几个Bean之间的互相引用: 甚至自己“循环”依赖自己:
那么在2.6.0之后,如果小伙伴依然觉得循环依赖无所谓,还坚持要用下面的这种模式: 那么,你将收获下面这样的报错: ┌─────┐ | a (field private com.example.demo.B 其实,Spring官方这样做,也是为了鼓励大家养成不要有循环依赖的好习惯。 但对于屎山项目,可能这样的要求对于开发者会很痛苦。 所以,你也可以通过下面的配置,放开不允许循环依赖的要求: spring.main.allow-circular-references=true 8. =ant-path-matcher 好了,关于Spring Boot 2.6的版本解析到这里结束了。 /spring-boot/wiki/Spring-Boot-2.6.0-Configuration-Changelog https://www.oschina.net/news/169783/spring-boot
如果在最后调用动态代理,这时候循环依赖的bean就不是动态代理的bean,所以要在之前就创建动态代理。所以这里有两个地方调用动态代理,实例化之后和初始化之后调用。 只在循环依赖的情况下在实例化之后创建动态代理,所以需要判断当前是不是循环依赖。 前面一级缓存中没拿到,而二级缓存中有,才是循环依赖。 而spring源码里有一行代码,用来判断是否是循环依赖,加了一个正在创建对象的标识。 无参构造函数 实例化 Object instanceBean = beanClass.newInstance(); // 创建动态代理 // 只在循环依赖的情况下在实例化之后创建动态代理 beanName); if (bean == null && singletonCurrennlyInCreation.contains(beanName)) { // 是循环依赖
写在前面 Spring 中常见的循环依赖有 3 种:单例 setter 循环依赖、单例构造方法循环依赖、原型循环依赖 关于单例 setter 循环依赖,Spring 是如何甄别和处理的,可查看: Spring 的循环依赖,源码详细分析 → 真的非要三级缓存吗 单例构造方法循环依赖 何谓单例构造方法循环依赖了,我们看具体代码就明白了 ? 问题就来了:Spring 是如何甄别单例情况下的构造方法循环依赖的,然后进行报错的 大家先把这个问题暂留在心里,我们再来看看什么是原型循环依赖 原型循环依赖 同样,我们直接看代码就明白何谓原型循环依赖了 是如何甄别单例情况下的构造方法循环依赖的 2、Spring 是如何甄别原型循环依赖的 3、为什么单例构造方法循环依赖和原型循环依赖的报错时机不一致 我们慢慢往下看,跟源码的过程可能比较快 参考 Spring 的循环依赖,源码详细分析 → 真的非要三级缓存吗
手写spring循环依赖的整个过程 2. spring怎么解决循环依赖 3. 为什么要二级缓存和三级缓存 4. spring有没有解决构造函数的循环依赖 5. spring有没有解决多例下的循环依赖. ---- 一. 什么是循环依赖? 如下图所示: ? 这也是循环依赖. , 形成了一个闭环 那么, 如果出现循环依赖, spring是如何解决循环依赖问题的呢? 二. 模拟循环依赖 2.1 复现循环依赖 我们定义三个类: 1. 在解析的过程中, 获取bean的时候会出现循环依赖的问题循环依赖. 下面就我们的代码分析一下: 第一种情况: 没有循环依赖 第二种情况: 有循环依赖 第三种情况: 有多次循环依赖 我们模拟一个循环依赖的场景, 覆盖这三种情况. ?
我们将探讨从Spring 5.1到Spring Boot 2.6版本中循环依赖处理逻辑的重要变化,分析不同注入方式对循环依赖的影响,并提供一套完整的最佳实践指南。 循环依赖检测机制演进 2.1 Spring Boot 2.6版本的重大变化 Spring Boot 2.6版本引入了一个重要变化:默认禁用了循环依赖。 图2:Spring循环依赖处理演进时间线 - 展示关键版本变化 2.2 配置项详解 # application.yml spring: main: # 是否允许循环依赖(Spring Boot /bin/bash # 循环依赖检测脚本 echo "=== Spring Boot 循环依赖检测 ===" # 检查Spring Boot版本 echo "检查Spring Boot版本..." 循环依赖指南 Spring Boot Actuator监控指南 关键词标签 Spring Boot 循环依赖 懒加载 依赖注入 架构设计
我们将探讨从Spring 5.1到Spring Boot 2.6版本中循环依赖处理逻辑的重要变化,分析不同注入方式对循环依赖的影响,并提供一套完整的最佳实践指南。 循环依赖检测机制演进2.1 Spring Boot 2.6版本的重大变化Spring Boot 2.6版本引入了一个重要变化:默认禁用了循环依赖。 /bin/bash# 循环依赖检测脚本echo "=== Spring Boot 循环依赖检测 ==="# 检查Spring Boot版本echo "检查Spring Boot版本..." 参考链接Spring Framework官方文档 - 循环依赖处理Spring Boot 2.6发布说明 - 循环依赖变更Martin Fowler - 依赖注入模式Baeldung - Spring循环依赖指南 Spring Boot Actuator监控指南关键词标签Spring Boot 循环依赖 懒加载 依赖注入 架构设计
循环依赖spring中将循环依赖处理分为了两种情况构造器循环依赖使用构造器注入构成循环依赖,这种方式无法进行解决,抛出了BeanCurrentlyInCreationException异常在创建bean TestB { private TestA testA; public TestB(TestA testA){ this.testA = testA; }}setter循环依赖对于 setter注入造成的循环依赖,spring采用的是提前暴露刚完成的构造器实例化但未完成setter方法注入的bean来实现的,而且只能解决单例作用域的beanjava 代码解读复制代码// ①doGetBean 存放的是完成实例化,但是还未进行属性注入的对象三级缓存 singletonFactories 提前暴露的一个单例工厂,二级缓存的对象就是通过这个单例工厂创建的有个疑问看上去好像二级缓存就可以解决循环依赖了 来看一下不管有没有循环依赖,都会创建好一个对象,然后放入到三级缓存中java 代码解读复制代码// 加入三级缓存addSingletonFactory(beanName, new ObjectFactory